import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * This class will be used for performing a number of benchmark-style tests
 * on the different hash functions and radix sort. You've been given quite 
 * a bit of code to get you started - fill in the rest from the instructions.
 * 
 * @author zach
 *
 */

/**
 * @author Matthew Dye
 * My part implements the instructions in the homework
 */
public class Main {

	public static void main(String[] args) throws FileNotFoundException{
		
		//Set everything up.
		Scanner scan;
		ArrayList<String> keys;
		ArrayList<Integer> vals;
		
		HashNode<String,Integer>[] arr, arr2;
		MSDRadixSorter<Integer> rs;
		HashTable<String,Integer> ht;

		/*
		 * @TODO: add your own function
		 */
		HashFunction[] hfs = {new SumHash(), new JavaHash(), new PearsonHash(), new SHA1Hash(), new MyHash()};
		String[] hashnames = {"Sum Hash", "Java Hash", "Pearson Hash", "SHA-1 Hash", "My Own Hash"};
		long startTime, endTime;
		double maxLoad = .7;
		int startSize = 89;
		int[] numElementsArr = {10, 100, 1000, 5000, 10000, 50000};
		int numElements;

		//Run once for every size
		for (int numind = 0; numind < numElementsArr.length; numind++){
			
			numElements = numElementsArr[numind];
			
			//Read everything in
			scan = new Scanner(new File("names.txt"));
			vals = new ArrayList<Integer>();
			keys = new ArrayList<String>();
			
			for (int i = 0; i < numElements; i++){
				keys.add(scan.next());
			}

			scan = new Scanner(new File("primeslist.txt"));
			for (int i = 0; i < numElements; i++){
				vals.add(Integer.parseInt(scan.next()));
			}
			
			//Mix it up a bit.
			java.util.Collections.shuffle(vals);
			java.util.Collections.shuffle(keys);
			
			rs = new MSDRadixSorter<Integer>();
			arr = (HashNode<String,Integer>[])new HashNode[numElements]; //Generics are a tricky beast.

			System.out.println("\n\n**********Number of elements: " + keys.size() + " **********");

			//Try out every hash function
			for (int ind = 0; ind < hashnames.length; ind++){
				startTime = System.currentTimeMillis();
				ht = new HashTable<String, Integer>(startSize, maxLoad, hfs[ind]);
				for (int i = 0; i < keys.size(); i++){
					ht.add(keys.get(i), vals.get(i));
				}
				endTime = System.currentTimeMillis();
				System.out.println("Unsorted " + hashnames[ind] + " time: " + (double)(endTime-startTime)/1000);
				System.out.println("Unsorted " + hashnames[ind] + " collisions: " + ht.collisionCount() + "\n");
			}

			for (int i = 0; i < keys.size(); i++){
				arr[i] = new HashNode<String,Integer>(keys.get(i), vals.get(i));
			}

			//Sort the array
			startTime = System.currentTimeMillis();
			arr2 = rs.sort(arr);
			endTime = System.currentTimeMillis();
			System.out.println("Time to sort keys: " + (double)(endTime-startTime)/1000);

			//Try every hash function again - think about why this could matter
			for (int ind = 0; ind < hashnames.length; ind++){
				startTime = System.currentTimeMillis();
				ht = new HashTable<String, Integer>(startSize, maxLoad, hfs[ind]);
				for (int i = 0; i < keys.size(); i++){
					ht.add(keys.get(i), vals.get(i));
				}
				endTime = System.currentTimeMillis();
				System.out.println("Sorted " + hashnames[ind] + " time: " + (double)(endTime-startTime)/1000);
				System.out.println("Sorted " + hashnames[ind] + " collisions: " + ht.collisionCount() + "\n");
			}
		}
		
		/*
		 * @TODO: add more tests as specified in the pdf
		 */
		
		
		// Testing Lookup Time
			scan = new Scanner(new File("names.txt"));
			vals = new ArrayList<Integer>();
			keys = new ArrayList<String>();
			String[] keyNames = new String[50000];
			for (int i = 0; i < 50000; i++){
				keys.add(scan.next());
			}
			scan = new Scanner(new File("names.txt"));
			for (int i = 0; i < 50000; i++){
				keyNames[i] = (scan.next());
			}
			scan = new Scanner(new File("primeslist.txt"));
			for (int i = 0; i < 50000; i++){
				vals.add(Integer.parseInt(scan.next()));
			}
			
			//Mix it up a bit.
			
			java.util.Collections.shuffle(vals);
			java.util.Collections.shuffle(keys);

				ht = new HashTable<String, Integer>(startSize, maxLoad, new JavaHash());
				for (int i = 0; i < keys.size(); i++){
					ht.add(keys.get(i), vals.get(i));
				}
				
			
			startTime = System.currentTimeMillis();
			for(int i = 0; i<50000; i = i+2)
			{
				ht.get(keyNames[i]);
				
			}
			endTime = System.currentTimeMillis();
			System.out.println("Time to LookUp using HashTable Unsorted: " + (double)(endTime-startTime)/1000);
			
			
			//Second Task on Array
			arr = (HashNode<String,Integer>[])new HashNode[50000]; //Generics are a tricky beast.

			rs = new MSDRadixSorter<Integer>();

			for (int i = 0; i < keys.size(); i++){
				arr[i] = new HashNode<String,Integer>(keys.get(i), vals.get(i));
			}
			arr2 = rs.sort(arr);
			ht = new HashTable<String, Integer>(startSize, maxLoad, new JavaHash());
			for (int i = 0; i < keys.size(); i++){
				ht.add(keys.get(i), vals.get(i));
			}
			startTime = System.currentTimeMillis();
			int  temp;
			for(int i = 0; i<50000; i = i+2)
			{
				temp = arr2[i].getValue();
				
			}
			endTime = System.currentTimeMillis();
			System.out.println("Time to LookUp using HashTable Sorted: " + (double)(endTime-startTime)/1000);
			
			//Task 3
			
			String[] newKeys = new String[1000];
			for(int i = 0; i<newKeys.length; i++)
			{
				newKeys[i] = Integer.toString(i);
			}
			
			
			startTime = System.currentTimeMillis();
			for (int i = 0; i < newKeys.length; i++){
				ht.add(newKeys[i], null);
			}
			endTime = System.currentTimeMillis();
			System.out.println("Time to add to HashTable " + (double)(endTime-startTime)/1000);
			
			//Task 4
			startTime = System.currentTimeMillis();
			for (int i = 0; i < newKeys.length; i++)
			{
				arr2 = rs.add(newKeys[i],null,arr2);
			}
			endTime = System.currentTimeMillis();
			
			System.out.println("Time to add to Sorted Array " + (double)(endTime-startTime)/1000);
			
	}

}
